package cz.vutbr.fit.xzemko01.tfmcbs.validation.validators;

import java.util.Map;

import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;

import cz.vutbr.fit.xzemko01.tfmcbs.model.Component;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Interface;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Relationship;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Requier;
import cz.vutbr.fit.xzemko01.tfmcbs.model.RequiringPort;
import cz.vutbr.fit.xzemko01.tfmcbs.model.SimpleComponent;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Usage;
import cz.vutbr.fit.xzemko01.tfmcbs.validation.utils.DiagnosticBuilder;

public class UsageValidator implements Validator<Usage> {
	private static UsageValidator usageValidator;
	
	private Validator<Relationship> relationshipValidator;
	
	private UsageValidator() {
		relationshipValidator = RelationshipValidator.getInstance();
	}
	
	public static Validator<Usage> getInstance() {
		if(usageValidator == null) {
			usageValidator = new UsageValidator();
		}
		
		return usageValidator;
	}
	
	@Override
	public boolean validate(Usage usage, DiagnosticChain diagnostics, Map<Object, Object> context) {
		boolean result = relationshipValidator.validate(usage, diagnostics, context);
		result &= validateUsageHasSetInterface(usage, diagnostics, context);
		result &= validateUsageHasSetRequier(usage, diagnostics, context);
		result &= validateUsageHasRequierAndInterfaceInSameContainer(usage, diagnostics, context);
		return result;
	}
	
	private boolean validateUsageHasSetInterface(Usage usage, DiagnosticChain diagnostics, Map<Object, Object> context) {
		if(usage.getInterface() == null) {
			Diagnostic diagnostic = new DiagnosticBuilder(usage)
				.setMessage("Usage \"" + usage.getName() + "\" has not set interface.").build();
			
			diagnostics.add(diagnostic);
			return false;
		}
		return true;
	}
	
	private boolean validateUsageHasSetRequier(Usage usage, DiagnosticChain diagnostics, Map<Object, Object> context) {
		if(usage.getRequier() == null) {
			Diagnostic diagnostic = new DiagnosticBuilder(usage)
				.setMessage("Usage with name \"" + usage.getName() + "\" has not set requier.").build();
			
			diagnostics.add(diagnostic);
			return false;
		}
		return true;
	}
	
	private boolean validateUsageHasRequierAndInterfaceInSameContainer(Usage usage, DiagnosticChain diagnostics, Map<Object, Object> context) {
		Requier requier = usage.getRequier();
		Interface _interface = usage.getInterface();
		if(requier == null || _interface == null) {
			return true;
		}
		
		boolean result = true;
		if(requier instanceof RequiringPort) {
			
			Component component = ((RequiringPort) requier).getComponent(); 
			if(component != null && component.getContainer() != _interface.getContainer()) {
				String errorMessage = 
						"Interface \"" + _interface.getName() + "\"" +
						" and port \"" + ((RequiringPort) requier).getName() + "\" " +
						"in the component \"" + component.getName() + "\" must be in same container.";
				
				Diagnostic diagnostic = new DiagnosticBuilder(usage).setMessage(errorMessage).build();
				diagnostics.add(diagnostic);
				result = false;
			}
		} else if(requier instanceof SimpleComponent) {
			SimpleComponent component = (SimpleComponent) requier;
			if(component.getContainer() != _interface.getContainer()) {
				String errorMessage = 
						"Interface \"" + _interface.getName() + "\"" +
						" and component \"" + component.getName() + "\" must be in same container.";
				
				Diagnostic diagnostic = new DiagnosticBuilder(usage).setMessage(errorMessage).build();
				diagnostics.add(diagnostic);
				result = false;
			}
		}
		
		return result;
	}
}